﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;

namespace Tencent.OA.App
{
    /// <summary>
    /// <para> 提供一个调度任务的执行处理。</para>
    /// </summary>
    public class ScheduleTask : IDisposable
    {
        private IScheduleService _scheduleService;
        private DateTime _executingTime;
        private Timer _timer = null;
        private int _interval = 5;
        private bool _absoluteExecuting;
        private bool _disposed;
        private bool _executeOnServiceStart;
        private bool _enableShutdown;

        private DateTime? _firstExecuteTime;
        private DateTime? _lastestCompleteTime;

        /// <summary>
        /// <para> 通过指定的调度实例及调度执行的间隔时间初始化一个新实例。</para>
        /// </summary>
        /// <param name="scheduleService">需要执行的调度任务。</param>
        /// <param name="interval">执行调度任务 <paramref name="scheduleService"/> 的间隔时间。</param>
        public ScheduleTask(IScheduleService scheduleService, int interval)
            : this(scheduleService, interval, null)
        {
        }

        /// <summary>
        /// <para> 通过指定的调度实例及每天执行调度的固定时间初始化一个新实例。</para>
        /// </summary>
        /// <param name="scheduleService">需要执行的调度任务。</param>
        /// <param name="executingTime">每天执行这个调度任务的固定时间。</param>
        public ScheduleTask(IScheduleService scheduleService, DateTime executingTime)
            : this(scheduleService, executingTime, 1 /* 每分钟扫描一次。*/, true, null)
        {
        }

        /// <summary>
        /// <para> 通过指定的调度实例及调度执行的间隔时间、配置值初始化一个新实例。</para>
        /// </summary>
        /// <param name="scheduleService">需要执行的调度任务。</param>
        /// <param name="interval">执行调度任务 <paramref name="scheduleService"/> 的间隔时间。</param>
        /// <param name="setting">用于这个调度任务服务提供程序的配置设置。</param>
        public ScheduleTask(IScheduleService scheduleService, int interval, ScheduleServiceProviderElement setting)
            : this(scheduleService, DateTime.Now, interval, false, setting)
        {
        }

        /// <summary>
        /// 创建新实例。
        /// </summary>
        /// <param name="scheduleService">需要执行的调度任务。</param>
        /// <param name="executingTime">每天执行这个调度任务的固定时间。</param>
        /// <param name="interval">执行调度任务 <paramref name="scheduleService"/> 的间隔时间。</param>
        /// <param name="absoluteExecuting">为true则指示是在每天固定时间执行，否则为间隔时间执行。</param>
        /// <param name="setting">用于这个调度任务服务提供程序的配置设置。</param>
        private ScheduleTask(IScheduleService scheduleService, DateTime executingTime, int interval, bool absoluteExecuting, ScheduleServiceProviderElement setting)
        {
            this._scheduleService = scheduleService;
            this._executingTime = executingTime;
            this._interval = interval;
            this._absoluteExecuting = absoluteExecuting;

            if (setting != null)
            {
                this._executeOnServiceStart = setting.ExecuteOnStart;
                this._enableShutdown = setting.EnableShutDown;
            }

            this._timer = new Timer();
            this._timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
        }

        /// <summary>
        /// 获取执行调度任务的时间间隔，单位为毫秒。
        /// </summary>
        protected virtual double Interval
        {
            get { return (double)(this._interval * 60000.00); }
        }

        /// <summary>
        /// 获取Timer的实例引用。
        /// </summary>
        internal Timer ExecutiveHandle
        {
            get { return this._timer; }
        }

        /// <summary>
        /// 获取这个任务开始执行的时间。
        /// </summary>
        public DateTime StartExecuteTime { get { return this._executingTime; } }

        /// <summary>
        /// 获取首次执行次任务的时间。
        /// </summary>
        public DateTime? FirstExecuteTime { get { return this._firstExecuteTime; } }

        /// <summary>
        /// 获取最近一次执行此任务的时间。
        /// </summary>
        public DateTime? LastestCompleteTime { get { return this._lastestCompleteTime; } }

        /// <summary>
        /// 当Timer的间隔时间到期时执行的这个调度任务。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void OnTimedEvent(object sender, ElapsedEventArgs e)
        {
            this.Execute();
            this._lastestCompleteTime = e.SignalTime;
        }

        // 执行这个任务。
        private void Execute()
        {
            // 首先检查是否是在固定时间执行调度，
            // 如果是则检查设定的时间和系统当前相比较，如果相差在60秒之内，这继续执行
            //
            // 设定为60秒的理由：在构建此类的实例时，已将this._interval设置为1分钟。则调度每隔一分钟扫描一次。
            //
            if (this._absoluteExecuting && !ExecutableTiming(this._executingTime))
            {
                return;
            }

            if (!this._firstExecuteTime.HasValue)
                this._firstExecuteTime = DateTime.Now;

            this._scheduleService.Execute();
        }

        // 检查是否已到达执行固定时间任务的时间点。误差时间为60秒。
        private bool ExecutableTiming(DateTime left)
        {
            int offset = 60; // 误差60秒。
            long interval = (long)((TimeSpan)(left - DateTime.Now)).TotalSeconds;
            return interval > 0 && interval <= offset;
        }

        /// <summary>
        /// 启动这个调度任务。
        /// </summary>
        public void Start()
        {
            if (this._executeOnServiceStart)
                this.Execute();

            this._timer.Interval = this.Interval;
            this._timer.Start();
        }

        /// <summary>
        /// 执行与释放或重置非托管资源。
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// <para> 指示是否释放非托管资源。</para>
        /// </summary>
        /// <param name="disposing">为true时释放非托管资源，false则不释放。</param>
        protected virtual void Dispose(bool disposing)
        {
            if (!this._disposed)
            {
                if (disposing)
                {
                    // 释放非托管资源
                    this._timer.Stop();
                    this._timer.Dispose();
                }

                this._disposed = true;
            }
        }

        /// <summary>
        /// 析构函数。
        /// </summary>
        ~ScheduleTask()
        {
            Dispose(false);
        }

        #region IDisposable 成员

        void IDisposable.Dispose()
        {
            this.Dispose();
        }

        #endregion
    }
}
